package controller

import (
	"context"
	"fmt"
	"path"
	"strconv"
	"time"

	"device-admin/config/redis"
	"device-admin/internal/app/grpc/service/robot"
	"device-admin/internal/app/grpc/service/terminal"
	"device-admin/internal/app/web/service/device"
	"device-admin/internal/define"

	device_grpc "device-admin/internal/app/grpc/service/device"

	"192.168.1.75/go-pkg/errorx"
	"192.168.1.75/go-pkg/helper"
	"192.168.1.75/go-pkg/logx"
	"192.168.1.75/go-pkg/response"
	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
)

type Monitor struct{}

// GetDeviceLogType 设备日志类型
func (Monitor) GetDeviceLogTypes(c *gin.Context) {
	type logNamesSt struct {
		ID   int    `json:"id"`
		Name string `json:"name"`
	}
	var logNames []logNamesSt
	for k, v := range define.DeviceLogType {
		logNames = append(logNames, logNamesSt{
			ID:   int(k),
			Name: string(v),
		})
	}
	response.JSON(c, logNames, nil)
}

// GetDeviceLogCategory 设置日志分类
func (Monitor) GetDeviceLogCategory(c *gin.Context) {
	type logTypesSt struct {
		ID   int    `json:"id"`
		Name string `json:"name"`
	}
	var logTypes []logTypesSt
	for k, v := range define.DeviceLogCategory {
		logTypes = append(logTypes, logTypesSt{
			ID:   int(k),
			Name: string(v),
		})
	}
	response.JSON(c, logTypes, nil)
}

// StopPushFlow 停止推流
func (Monitor) StopPushFlow(c *gin.Context) {
	var params struct {
		RobotID string `json:"robot_id"` // 机器id
		CamType int    `json:"cam_type"` // 摄像头类型
	}
	c.ShouldBindBodyWith(&params, binding.JSON)
	// 如果当前用户正在观看摄像头画面，则删除观看标志，并做延迟，以便进行一次关流（由会话定时任务自动检测执行）
	authorization := c.GetString("authorization")
	viewerID := define.RedisDeviceCameraViewer + params.RobotID + ":" + authorization + ":" + strconv.Itoa(params.CamType)
	count, _ := redis.NewDB().Exists(context.Background(), viewerID).Result()
	if count > 0 {
		// 删除用户观看摄像头的标志
		redis.NewDB().Del(context.Background(), viewerID)
	}
	response.JSON(c, nil, nil)
}

// PushFlow 推流
func (Monitor) PushFlow(c *gin.Context) {
	var params struct {
		RobotID string `json:"robot_id"` // 机器id
		CamType int    `json:"cam_type"` // 摄像头类型
	}
	c.ShouldBindBodyWith(&params, binding.JSON)
	// 如果当前用户正在观看摄像头画面，则删除观看标志，并做延迟，以便进行一次关流（由会话定时任务自动检测执行）
	authorization := c.GetString("authorization")
	viewerID := define.RedisDeviceCameraViewer + params.RobotID + ":" + authorization + ":" + strconv.Itoa(params.CamType)
	count, _ := redis.NewDB().Exists(context.Background(), viewerID).Result()
	if count > 0 {
		// 删除用户观看摄像头的标志
		redis.NewDB().Del(context.Background(), viewerID)
		// 延迟2s,等待上次推流关闭（设备定时任务调度）
		time.Sleep(time.Second * 2)
		// 设置用户观看摄像头标志
		redis.NewDB().SetEX(context.Background(), viewerID, 1, time.Second*1)
	}
	var err error
	if helper.CompareVersion("V", c.GetString("device_firmware_version"), "V1.65", "") == 2 {
		// 新的98终端推流，多路是分开控制
		// 所以当摄像头类型为0时，同时进行多路推流
		if params.CamType == 0 {
			robot.WildmsgPushFlow(params.RobotID, c.GetString("username"), 3)
			robot.WildmsgPushFlow(params.RobotID, c.GetString("username"), 4)
			robot.WildmsgPushFlow(params.RobotID, c.GetString("username"), 5)
		} else {
			err = robot.WildmsgPushFlow(params.RobotID, c.GetString("username"), params.CamType)
		}
	} else {
		err = robot.PushFlowCMDPack(params.RobotID, c.GetString("username"), params.CamType)
	}
	response.JSON(c, nil, err)
}

// StartTask 启动任务
func (Monitor) StartTask(c *gin.Context) {
	var params struct {
		RobotID  string `json:"robot_id"`
		MapName  string `json:"map_name"`
		TaskName string `json:"task_name"`
		RunCount int    `json:"run_count"`
	}
	c.ShouldBindBodyWith(&params, binding.JSON)
	var err error
	if helper.CompareVersion("V", c.GetString("device_firmware_version"), "V1.65", "") == 2 {
		err = robot.WildmsgStartTask(params.RobotID, params.MapName, params.TaskName, params.RunCount, c.GetString("username"))
	} else {
		err = robot.StartTaskCMDPack(params.RobotID, params.MapName, params.TaskName, params.RunCount, c.GetString("username"))
	}

	response.JSON(c, nil, err)
}

// PauseTask 暂停任务
func (Monitor) PauseTask(c *gin.Context) {
	var params struct {
		RobotID string `json:"robot_id"`
		TaskID  string `json:"task_id"`
	}
	c.ShouldBindBodyWith(&params, binding.JSON)
	var err error
	if helper.CompareVersion("V", c.GetString("device_firmware_version"), "V1.65", "") == 2 {
		err = robot.WildmsgPauseTask(params.RobotID, params.TaskID, c.GetString("username"))
	} else {
		err = robot.PauseCurrentTaskCMDPack(params.RobotID, params.TaskID, c.GetString("username"))
	}
	response.JSON(c, nil, err)
}

// EndTask 终止任务
func (Monitor) EndTask(c *gin.Context) {
	var params struct {
		RobotID string `json:"robot_id"`
		TaskID  string `json:"task_id"`
	}
	c.ShouldBindBodyWith(&params, binding.JSON)
	err := robot.WildmsgEndTask(params.RobotID, params.TaskID, c.GetString("username"))
	response.JSON(c, nil, err)
}

// ResumeTask 停止任务
func (Monitor) StopTask(c *gin.Context) {
	var params struct {
		RobotID       string `json:"robot_id"`
		MapName       string `json:"map_name"`
		TaskName      string `json:"task_name"`
		TaskEffection string `json:"task_effection"`
	}
	c.ShouldBindBodyWith(&params, binding.JSON)
	var err error
	if helper.CompareVersion("V", c.GetString("device_firmware_version"), "V1.65", "") == 2 {
		err = robot.WildmsgTaskEffectivenessCMD(params.RobotID, params.MapName, params.TaskName, "no")
	} else {
		err = robot.TaskEffectivenessTaskCMDPack(params.RobotID, params.MapName, params.TaskName, "no")
	}
	response.JSON(c, nil, err)
}

// ResumeTask 恢复任务
func (Monitor) ResumeTask(c *gin.Context) {
	var params struct {
		RobotID       string `json:"robot_id"`
		MapName       string `json:"map_name"`
		TaskName      string `json:"task_name"`
		TaskEffection string `json:"task_effection"`
	}
	c.ShouldBindBodyWith(&params, binding.JSON)
	var err error
	if helper.CompareVersion("V", c.GetString("device_firmware_version"), "V1.65", "") == 2 {
		err = robot.WildmsgTaskEffectivenessCMD(params.RobotID, params.MapName, params.TaskName, "yes")
	} else {
		err = robot.TaskEffectivenessTaskCMDPack(params.RobotID, params.MapName, params.TaskName, "yes")
	}
	response.JSON(c, nil, err)
}

// ContinueTask 继续任务
func (Monitor) ContinueTask(c *gin.Context) {
	var params struct {
		RobotID string `json:"robot_id"`
	}
	c.ShouldBindBodyWith(&params, binding.JSON)
	var err error
	if helper.CompareVersion("V", c.GetString("device_firmware_version"), "V1.65", "") == 2 {
		err = robot.WildmsgResumeExceptionTask(params.RobotID, c.GetString("username"), "")
	} else {
		err = robot.ResumeExceptionTaskCMDPack(params.RobotID, c.GetString("username"), "")
	}

	response.JSON(c, nil, err)
}

// ResumeExceptionTask 恢复异常任务
func (Monitor) ResumeExceptionTask(c *gin.Context) {
	var params struct {
		RobotID string `json:"robot_id"`
	}
	c.ShouldBindBodyWith(&params, binding.JSON)
	var err error
	if helper.CompareVersion("V", c.GetString("device_firmware_version"), "V1.65", "") == 2 {
		err = robot.WildmsgResumeExceptionTask(params.RobotID, c.GetString("username"), "ExceptionRecovery")
	} else {
		err = robot.ResumeExceptionTaskCMDPack(params.RobotID, c.GetString("username"), "ExceptionRecovery")
	}
	response.JSON(c, nil, err)
}

// AutoRelocation 自动重定位
func (Monitor) AutoRelocation(c *gin.Context) {
	var params struct {
		RobotID string `json:"robot_id"`
	}
	c.ShouldBindBodyWith(&params, binding.JSON)
	var err error
	if helper.CompareVersion("V", c.GetString("device_firmware_version"), "V1.65", "") == 2 {
		err = robot.WildmsgAutoRelocation(params.RobotID, c.GetString("username"))
	} else {
		err = robot.AutoRelocationCMDPack(params.RobotID, c.GetString("username"))
	}
	response.JSON(c, nil, err)
}

// Relocation 手动重新定位
func (Monitor) Relocation(c *gin.Context) {
	// 手动定位参数，theta-弧度值,x坐标，y坐标
	var params struct {
		RobotID string   `json:"robot_id"`
		X       *float64 `json:"x"`
		Y       *float64 `json:"y"`
		Theta   *float64 `json:"theta"`
	}
	c.ShouldBindBodyWith(&params, binding.JSON)

	x := *params.X
	y := *params.Y
	theta := *params.Theta
	if params.X == nil {
		x = 0
	}
	if params.Y == nil {
		y = 0
	}
	if params.Theta == nil {
		theta = 0
	}
	var err error
	if helper.CompareVersion("V", c.GetString("device_firmware_version"), "V1.65", "") == 2 {
		err = robot.WildmsgManualRelocation(params.RobotID, x, y, theta, c.GetString("username"))
	} else {
		err = robot.PoseRelocationCMDPack(params.RobotID, x, y, theta, c.GetString("username"))
	}
	response.JSON(c, nil, err)
}

// Shutdown 关机
func (Monitor) Shutdown(c *gin.Context) {
	var params struct {
		RobotID string `json:"robot_id"`
	}
	c.ShouldBindBodyWith(&params, binding.JSON)
	var err error
	if helper.CompareVersion("V", c.GetString("device_firmware_version"), "V1.65", "") == 2 {
		err = robot.WildmsgShutdown(params.RobotID, c.GetString("username"))
	} else {
		err = robot.ShutdownCMDPack(params.RobotID, c.GetString("username"))
	}
	response.JSON(c, nil, err)
}

// SetCharge 去充电
func (Monitor) SetCharge(c *gin.Context) {
	var params struct {
		RobotID string `json:"robot_id"`
	}
	c.ShouldBindBodyWith(&params, binding.JSON)
	var err error
	if helper.CompareVersion("V", c.GetString("device_firmware_version"), "V1.65", "") == 2 {
		err = robot.WildmsgCharge(params.RobotID, c.GetString("username"))
	} else {
		err = robot.ChargeCMDPack(params.RobotID, c.GetString("username"))
	}
	response.JSON(c, nil, err)
}

// SetDeviceOpt 配置设置
// 这里主要做重启远程排障操作
func (Monitor) SetDeviceOpt(c *gin.Context) {
	var params struct {
		RobotID    string `json:"robot_id"`
		DeviceType int    `json:"device_type"`
		Opt        int    `json:"opt"`
	}
	c.ShouldBindBodyWith(&params, binding.JSON)
	err := robot.WildmsgSetDeviceOpt(params.RobotID, params.DeviceType, params.Opt)
	response.JSON(c, nil, err)
}

// TakeOver 人工接管
func (Monitor) TakeOver(c *gin.Context) {
	var params struct {
		RobotID string `json:"robot_id"`
		Status  int    `json:"status"`
	}
	c.ShouldBindBodyWith(&params, binding.JSON)
	var err error
	if helper.CompareVersion("V", c.GetString("device_firmware_version"), "V1.65", "") == 2 {
		err = robot.WildmsgTaskOver(params.RobotID, params.Status, c.GetString("username"))
	} else {
		err = robot.TakeOverCMDPack(params.RobotID, params.Status, c.GetString("username"))
	}
	if err != nil {
		response.JSON(c, nil, err)
		return
	}
}

// SetDoorStatus 远程控制电子锁
func (Monitor) SetDoorStatus(c *gin.Context) {
	var params struct {
		RobotID  string `json:"robot_id"`
		DoorType int    `json:"door_type"`
		Status   int    `json:"status"`
	}
	c.ShouldBindBodyWith(&params, binding.JSON)
	err := device_grpc.DoorCtlCMDPack(params.RobotID, params.DoorType, params.Status)
	response.JSON(c, nil, err)
}

// SetLiftPosition 设置升降盘位置
func (Monitor) SetLiftPosition(c *gin.Context) {
	var params struct {
		RobotID string `json:"robot_id"`
		Floor   int    `json:"floor"`
	}
	c.ShouldBindBodyWith(&params, binding.JSON)
	err := device_grpc.LiftPostionCMDPack(params.RobotID, params.Floor)
	response.JSON(c, nil, err)
}

// GetOrderVideoInfo 获取订单视频信息
func (Monitor) GetOrderVideoInfo(c *gin.Context) {
	robotID := c.Query("robot_id") // 设备id
	orderID := c.Query("order_id") // 订单id
	fileName, fileSize, md5, err := robot.GetOrderVideoInfo(robotID, orderID)
	// 视频文件太大
	if fileSize > 40*1024*1024 {
		response.JSON(c, nil, errorx.New("视频文件太大", -1))
		return
	}
	// 返回视频文件信息
	response.JSON(c, map[string]interface{}{
		"file_name": fileName,
		"file_size": fileSize,
		"md5":       md5,
	}, err)
}

// GetOrderVideo 获取订单视频
func (Monitor) GetOrderVideo(c *gin.Context) {
	robotID := c.Query("robot_id") // 设备id
	orderID := c.Query("order_id") // 订单id
	filename, fileSize, _, err := robot.GetOrderVideoInfo(robotID, orderID)
	// 视频超过20M时，判定为异常
	if fileSize > 40*1024*1024 {
		response.JSON(c, nil, errorx.New("视频文件太大", -1))
		return
	}
	// 如果没有异常时
	if err == nil {
		// 设置响应头
		c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filename))
		c.Header("Content-Type", "video/"+path.Ext(filename))
		c.Header("Content-Length", strconv.Itoa(int(fileSize)))
		// 分批次读取文件内容，每次300KB，并将结果写入到缓冲区中
		chunkSize := int64(300 * 1024)
		for size := int64(0); size < fileSize; size += chunkSize {
			select {
			case <-c.Request.Context().Done():
				logx.Error(context.Background(), "GetOrderVideo request interrupted", logx.String("robot_id", robotID), logx.Err(err))
				return
			default:
				// 避免下载的文件还未结束录制，导致文件内容超过了一开始的大小
				if (fileSize - size) < chunkSize {
					chunkSize = fileSize - size
				}
				content, err := robot.GetOrderVideoContent(robotID, size, chunkSize)
				if err != nil {
					logx.Error(context.Background(), "GetOrderVideoInfo Error", logx.String("robot_id", robotID), logx.Err(err))
					return
				}
				c.Writer.Write(content)
			}
		}
	} else {
		logx.Error(context.Background(), "GetOrderVideoInfo Error", logx.String("robot_id", robotID), logx.Err(err))
		response.JSON(c, nil, err)
	}
}

// PlayTTS 控制设备播放tts
func (Monitor) PlayTTS(c *gin.Context) {
	var params struct {
		RobotID string `json:"robot_id"`
		Text    string `json:"text"`
	}
	c.ShouldBindBodyWith(&params, binding.JSON)
	err := terminal.PlayTTS(params.RobotID, params.Text)
	response.JSON(c, nil, err)
}

// AddQuickReply 新增快捷回复
func (Monitor) AddQuickReply(c *gin.Context) {
	var params struct {
		Text string `json:"text"`
	}
	c.ShouldBindBodyWith(&params, binding.JSON)
	id, err := device.AddQuickReply(c.Request.Context(), c.GetString("merchant_id"), params.Text)
	if err != nil {
		response.JSON(c, map[string]interface{}{
			"id": "",
		}, err)
	} else {
		response.JSON(c, map[string]interface{}{
			"id": id,
		}, err)
	}
}

// EditQuickReply 编辑快捷回复
func (Monitor) EditQuickReply(c *gin.Context) {
	var params struct {
		ID   string `json:"id"`
		Text string `json:"text"`
	}
	c.ShouldBindBodyWith(&params, binding.JSON)
	err := device.EditQuickReply(c.Request.Context(), c.GetString("merchant_id"), params.ID, params.Text)
	response.JSON(c, nil, err)
}

// DeleteQuickReply 删除快捷回复
func (Monitor) DeleteQuickReply(c *gin.Context) {
	var params struct {
		ID string `json:"id"`
	}
	c.ShouldBindBodyWith(&params, binding.JSON)
	err := device.DeleteQuickReply(c.Request.Context(), c.GetString("merchant_id"), params.ID)
	response.JSON(c, nil, err)
}

// QuickReplyList 快捷回复列表
func (Monitor) QuickReplyList(c *gin.Context) {
	list, err := device.QuickReplyList(c.Request.Context(), c.GetString("merchant_id"), c.Query("text"))
	if err != nil {
		response.JSON(c, []string{}, err)
	} else {
		response.JSON(c, map[string]interface{}{
			"list":  list,
			"total": len(list),
		}, err)
	}
}

// SetVolume 设置音量
func (Monitor) SetVolume(c *gin.Context) {
	var params struct {
		RobotID       string `json:"robot_id"`
		TerminalType  string `json:"terminal_type"`
		VolumePercent int    `json:"volume_percent"`
		Volumetype    int    `json:"volume_type"`
	}
	c.ShouldBindBodyWith(&params, binding.JSON)
	err := device.Media{}.SetVolume(
		c.Request.Context(),
		params.RobotID, params.TerminalType,
		params.VolumePercent,
		params.Volumetype,
	)
	response.JSON(c, nil, err)
}

// SetVolume 设置音量
func (Monitor) GetVolume(c *gin.Context) {
	volumetype, _ := strconv.Atoi(c.Query("volume_type"))
	logx.Info(context.Background(), "GetVolume Volumetype", logx.Int("Volumetype", volumetype))
	volume, err := device.Media{}.GetVolume(
		c.Request.Context(),
		c.Query("robot_id"), c.Query("terminal_type"), volumetype,
	)
	response.JSON(c, map[string]interface{}{
		"volume_percent": volume,
	}, err)
}
